From 8f113e07fde88821907d75f6ed7803120a58bd5b Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Thu, 17 Jan 2013 20:59:07 +0100 Subject: [PATCH] Add GtkGesture This a more specific abstract type that handles one or multiple streams of pointer/touch events. --- gtk/Makefile.am | 2 + gtk/gtk.h | 1 + gtk/gtkenums.h | 7 + gtk/gtkgesture.c | 964 +++++++++++++++++++++++++++++++++++++++++ gtk/gtkgesture.h | 133 ++++++ gtk/gtkmarshalers.list | 1 + 6 files changed, 1108 insertions(+) create mode 100644 gtk/gtkgesture.c create mode 100644 gtk/gtkgesture.h diff --git a/gtk/Makefile.am b/gtk/Makefile.am index a6f73dfd90..b90c934a6f 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -280,6 +280,7 @@ gtk_public_h_sources = \ gtkfontchooserdialog.h \ gtkfontchooserwidget.h \ gtkframe.h \ + gtkgesture.h \ gtkgrid.h \ gtkheaderbar.h \ gtkicontheme.h \ @@ -772,6 +773,7 @@ gtk_base_c_sources = \ gtkfontchooserwidget.c \ gtkframe.c \ gtkgladecatalog.c \ + gtkgesture.c \ gtkgrid.c \ gtkheaderbar.c \ gtkhsla.c \ diff --git a/gtk/gtk.h b/gtk/gtk.h index 8eb33a6a2a..57e947b730 100644 --- a/gtk/gtk.h +++ b/gtk/gtk.h @@ -106,6 +106,7 @@ #include #include #include +#include #include #include #include diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h index 2787f2c040..95844dadd9 100644 --- a/gtk/gtkenums.h +++ b/gtk/gtkenums.h @@ -1049,4 +1049,11 @@ typedef enum GTK_INPUT_HINT_INHIBIT_OSK = 1 << 7 } GtkInputHints; +typedef enum +{ + GTK_EVENT_SEQUENCE_NONE, + GTK_EVENT_SEQUENCE_CLAIMED, + GTK_EVENT_SEQUENCE_DENIED +} GtkEventSequenceState; + #endif /* __GTK_ENUMS_H__ */ diff --git a/gtk/gtkgesture.c b/gtk/gtkgesture.c new file mode 100644 index 0000000000..27cc1f5670 --- /dev/null +++ b/gtk/gtkgesture.c @@ -0,0 +1,964 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2012, One Laptop Per Child. + * Copyright (C) 2014, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author(s): Carlos Garnacho + */ +#include "config.h" +#include +#include "gtktypebuiltins.h" +#include "gtkprivate.h" +#include "gtkmarshalers.h" +#include "gtkintl.h" + +typedef struct _GtkGesturePrivate GtkGesturePrivate; +typedef struct _PointData PointData; + +enum { + PROP_N_POINTS = 1, + PROP_TOUCH_ONLY, +}; + +enum { + CHECK, + BEGIN, + END, + UPDATE, + SEQUENCE_STATE_CHANGED, + N_SIGNALS +}; + +struct _PointData +{ + GdkEvent *event; + guint state : 2; +}; + +struct _GtkGesturePrivate +{ + GHashTable *points; + GdkEventSequence *last_sequence; + GdkWindow *window; + GdkDevice *device; + guint n_points; + guint recognized : 1; + guint touch_only : 1; +}; + +static guint signals[N_SIGNALS] = { 0 }; + +#define BUTTONS_MASK (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK) + +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GtkGesture, gtk_gesture, GTK_TYPE_EVENT_CONTROLLER) + +static void +gtk_gesture_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkGesturePrivate *priv = gtk_gesture_get_instance_private (GTK_GESTURE (object)); + + switch (prop_id) + { + case PROP_N_POINTS: + g_value_set_uint (value, priv->n_points); + break; + case PROP_TOUCH_ONLY: + g_value_set_boolean (value, priv->touch_only); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gtk_gesture_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkGesturePrivate *priv = gtk_gesture_get_instance_private (GTK_GESTURE (object)); + + switch (prop_id) + { + case PROP_N_POINTS: + priv->n_points = g_value_get_uint (value); + break; + case PROP_TOUCH_ONLY: + gtk_gesture_set_touch_only (GTK_GESTURE (object), + g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gtk_gesture_finalize (GObject *object) +{ + GtkGesturePrivate *priv = gtk_gesture_get_instance_private (GTK_GESTURE (object)); + + g_hash_table_destroy (priv->points); + + G_OBJECT_CLASS (gtk_gesture_parent_class)->finalize (object); +} + +static guint +_gtk_gesture_effective_n_points (GtkGesture *gesture) +{ + GtkGesturePrivate *priv; + GHashTableIter iter; + guint n_points = 0; + PointData *data; + + priv = gtk_gesture_get_instance_private (gesture); + g_hash_table_iter_init (&iter, priv->points); + + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &data)) + { + if (data->state == GTK_EVENT_SEQUENCE_DENIED) + continue; + if (data->event->type == GDK_TOUCH_END || + data->event->type == GDK_BUTTON_RELEASE) + continue; + + n_points++; + } + + return n_points; +} + +static gboolean +gtk_gesture_check_impl (GtkGesture *gesture) +{ + GtkGesturePrivate *priv; + guint n_points; + + priv = gtk_gesture_get_instance_private (gesture); + n_points = _gtk_gesture_effective_n_points (gesture); + + return n_points == priv->n_points; +} + +static void +_gtk_gesture_set_recognized (GtkGesture *gesture, + gboolean recognized, + GdkEventSequence *sequence) +{ + GtkGesturePrivate *priv; + + priv = gtk_gesture_get_instance_private (gesture); + + if (priv->recognized == recognized) + return; + + priv->recognized = recognized; + + if (recognized) + g_signal_emit (gesture, signals[BEGIN], 0, sequence); + else + g_signal_emit (gesture, signals[END], 0, sequence); +} + +static gboolean +_gtk_gesture_do_check (GtkGesture *gesture) +{ + gboolean retval; + + g_signal_emit (G_OBJECT (gesture), signals[CHECK], 0, &retval); + retval = retval != FALSE; + + return retval; +} + +static gboolean +_gtk_gesture_check_recognized (GtkGesture *gesture, + GdkEventSequence *sequence) +{ + GtkGesturePrivate *priv = gtk_gesture_get_instance_private (gesture); + guint current_n_points; + + current_n_points = _gtk_gesture_effective_n_points (gesture); + + if (priv->recognized && current_n_points != priv->n_points) + _gtk_gesture_set_recognized (gesture, FALSE, sequence); + else if (!priv->recognized && + current_n_points == priv->n_points && + _gtk_gesture_do_check (gesture)) + _gtk_gesture_set_recognized (gesture, TRUE, sequence); + + return priv->recognized; +} + +static gboolean +_gtk_gesture_update_point (GtkGesture *gesture, + const GdkEvent *event, + gboolean add) +{ + GdkEventSequence *sequence; + GtkGesturePrivate *priv; + GdkDevice *device; + PointData *data; + gdouble x, y; + + if (!gdk_event_get_coords (event, &x, &y)) + return FALSE; + + device = gdk_event_get_device (event); + + if (!device) + return FALSE; + + priv = gtk_gesture_get_instance_private (gesture); + + if (priv->device && priv->device != device) + return FALSE; + + sequence = gdk_event_get_event_sequence (event); + + if (!g_hash_table_lookup_extended (priv->points, sequence, + NULL, (gpointer *) &data)) + { + if (!add) + return FALSE; + + if (g_hash_table_size (priv->points) == 0) + { + priv->window = event->any.window; + priv->device = device; + } + + data = g_new0 (PointData, 1); + g_hash_table_insert (priv->points, sequence, data); + } + + if (data->event) + gdk_event_free (data->event); + + data->event = gdk_event_copy (event); + + return TRUE; +} + +static void +_gtk_gesture_remove_point (GtkGesture *gesture, + const GdkEvent *event) +{ + GdkEventSequence *sequence; + GtkGesturePrivate *priv; + GdkDevice *device; + + sequence = gdk_event_get_event_sequence (event); + device = gdk_event_get_device (event); + priv = gtk_gesture_get_instance_private (gesture); + + g_hash_table_remove (priv->points, sequence); + + if (device == priv->device && + g_hash_table_size (priv->points) == 0) + { + priv->window = NULL; + priv->device = NULL; + } +} + +static gboolean +gtk_gesture_handle_event (GtkEventController *controller, + const GdkEvent *event) +{ + GtkGesture *gesture = GTK_GESTURE (controller); + GdkEventSequence *sequence; + GtkGesturePrivate *priv; + + priv = gtk_gesture_get_instance_private (gesture); + sequence = gdk_event_get_event_sequence (event); + priv->last_sequence = sequence; + +#if 0 + if (event->any.window != priv->window) + return FALSE; +#endif + + switch (event->type) + { + case GDK_BUTTON_PRESS: + if (priv->touch_only) + break; + /* Fall through */ + case GDK_TOUCH_BEGIN: + if (_gtk_gesture_update_point (gesture, event, TRUE)) + _gtk_gesture_check_recognized (gesture, sequence); + break; + case GDK_BUTTON_RELEASE: + if (priv->touch_only) + break; + /* Fall through */ + case GDK_TOUCH_END: + if (_gtk_gesture_update_point (gesture, event, FALSE)) + { + if (_gtk_gesture_check_recognized (gesture, sequence)) + g_signal_emit (gesture, signals[UPDATE], 0, sequence); + + _gtk_gesture_remove_point (gesture, event); + } + break; + case GDK_MOTION_NOTIFY: + if (priv->touch_only) + break; + if ((event->motion.state & BUTTONS_MASK) == 0) + break; + + if (event->motion.is_hint) + gdk_event_request_motions (&event->motion); + + /* Fall through */ + case GDK_TOUCH_UPDATE: + if (_gtk_gesture_update_point (gesture, event, TRUE) && + _gtk_gesture_check_recognized (gesture, sequence)) + g_signal_emit (gesture, signals[UPDATE], 0, sequence); + break; + default: + /* Unhandled event */ + return FALSE; + } + + if (gtk_gesture_get_sequence_state (gesture, sequence) != GTK_EVENT_SEQUENCE_CLAIMED) + return FALSE; + + return priv->recognized; +} + +static void +gtk_gesture_class_init (GtkGestureClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass); + + object_class->get_property = gtk_gesture_get_property; + object_class->set_property = gtk_gesture_set_property; + object_class->finalize = gtk_gesture_finalize; + + controller_class->handle_event = gtk_gesture_handle_event; + + klass->check = gtk_gesture_check_impl; + + g_object_class_install_property (object_class, + PROP_N_POINTS, + g_param_spec_uint ("n-points", + P_("Number of points"), + P_("Number of points needed " + "to trigger the gesture"), + 1, G_MAXUINT, 1, + GTK_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_TOUCH_ONLY, + g_param_spec_boolean ("touch-only", + P_("Handle only touch events"), + P_("Whether the gesture handles" + " only touch events"), + TRUE, + GTK_PARAM_READWRITE)); + + signals[CHECK] = + g_signal_new ("check", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkGestureClass, check), + g_signal_accumulator_true_handled, + NULL, NULL, + G_TYPE_BOOLEAN, 0); + signals[BEGIN] = + g_signal_new ("begin", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkGestureClass, begin), + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_POINTER); + signals[END] = + g_signal_new ("end", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkGestureClass, end), + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_POINTER); + signals[UPDATE] = + g_signal_new ("update", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkGestureClass, update), + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_POINTER); + signals[SEQUENCE_STATE_CHANGED] = + g_signal_new ("sequence-state-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkGestureClass, sequence_state_changed), + NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_POINTER, + GTK_TYPE_EVENT_SEQUENCE_STATE); +} + +static void +_gtk_gesture_update_evmask (GtkGesture *gesture) +{ + GtkGesturePrivate *priv; + GdkEventMask evmask; + + priv = gtk_gesture_get_instance_private (gesture); + evmask = GDK_TOUCH_MASK; + + if (!priv->touch_only) + evmask |= GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_BUTTON_MOTION_MASK; + + gtk_event_controller_set_event_mask (GTK_EVENT_CONTROLLER (gesture), evmask); +} + +static void +gtk_gesture_init (GtkGesture *gesture) +{ + GtkGesturePrivate *priv; + + priv = gtk_gesture_get_instance_private (gesture); + priv->points = g_hash_table_new_full (NULL, NULL, NULL, + (GDestroyNotify) g_free); + + if (g_getenv ("GTK_TEST_TOUCHSCREEN")) + priv->touch_only = FALSE; + else + priv->touch_only = TRUE; + + _gtk_gesture_update_evmask (gesture); +} + +/** + * gtk_gesture_get_device: + * @gesture: a #GtkGesture + * + * Returns the master #GdkDevice that is currently operating + * on @gesture, or %NULL if the gesture is not being interacted. + * + * Returns: a #GdkDevice, or %NULL. + * + * Since: 3.14 + **/ +GdkDevice * +gtk_gesture_get_device (GtkGesture *gesture) +{ + GtkGesturePrivate *priv; + + g_return_val_if_fail (GTK_IS_GESTURE (gesture), NULL); + + priv = gtk_gesture_get_instance_private (gesture); + + return priv->device; +} + +/** + * gtk_gesture_get_sequence_state: + * @gesture: a #GtkGesture + * @sequence: a #GdkEventSequence + * + * Returns the @sequence state, as seen by @gesture. + * + * Returns: The sequence state in @gesture. + * + * Since: 3.14 + **/ +GtkEventSequenceState +gtk_gesture_get_sequence_state (GtkGesture *gesture, + GdkEventSequence *sequence) +{ + GtkGesturePrivate *priv; + PointData *data; + + g_return_val_if_fail (GTK_IS_GESTURE (gesture), + GTK_EVENT_SEQUENCE_NONE); + + priv = gtk_gesture_get_instance_private (gesture); + data = g_hash_table_lookup (priv->points, sequence); + + if (!data) + return GTK_EVENT_SEQUENCE_NONE; + + return data->state; +} + +/** + * gtk_gesture_set_sequence_state: + * @gesture: a #GtkGesture + * @sequence: a #GdkEventSequence + * @state: the sequence state + * + * Sets the state of @sequence in @gesture. + * + * Returns: #TRUE if @sequence is handled by @gesture. + * + * Since: 3.14 + **/ +gboolean +gtk_gesture_set_sequence_state (GtkGesture *gesture, + GdkEventSequence *sequence, + GtkEventSequenceState state) +{ + GtkGesturePrivate *priv; + PointData *data; + guint old_state; + + g_return_if_fail (GTK_IS_GESTURE (gesture)); + g_return_if_fail (state >= GTK_EVENT_SEQUENCE_NONE && + state <= GTK_EVENT_SEQUENCE_DENIED); + + priv = gtk_gesture_get_instance_private (gesture); + data = g_hash_table_lookup (priv->points, sequence); + + if (!data || data->state == state) + return FALSE; + + old_state = data->state; + data->state = state; + g_signal_emit (gesture, signals[SEQUENCE_STATE_CHANGED], 0, + sequence, old_state); + return TRUE; +} + +/** + * gtk_gesture_get_sequences: + * @gesture: a #GtkGesture + * + * Returns the list of #GdkEventSequences currently being interpreted + * by @gesture + * + * Returns: (transfer container) (element-type:Gdk.EventSequence): A list + * of #GdkEventSequences, the list elements are owned by GTK+ + * and must not be freed or modified, the list itself must be deleted + * through g_list_free() + **/ +GList * +gtk_gesture_get_sequences (GtkGesture *gesture) +{ + GdkEventSequence *sequence; + GtkGesturePrivate *priv; + GList *sequences = NULL; + GHashTableIter iter; + PointData *data; + + g_return_val_if_fail (GTK_IS_GESTURE (gesture), NULL); + + priv = gtk_gesture_get_instance_private (gesture); + g_hash_table_iter_init (&iter, priv->points); + + while (g_hash_table_iter_next (&iter, (gpointer *) &sequence, (gpointer *) &data)) + { + if (data->state == GTK_EVENT_SEQUENCE_DENIED) + continue; + if (data->event->type == GDK_TOUCH_END || + data->event->type == GDK_BUTTON_RELEASE) + continue; + + sequences = g_list_prepend (sequences, sequence); + } + + return sequences; +} + +/** + * gtk_gesture_get_last_updated_sequence: + * @gesture: a #GtkGesture + * + * Returns the #GdkEventSequence that was last updated on @gesture. + * + * Returns: The last updated sequence. + * + * Since: 3.14 + **/ +GdkEventSequence * +gtk_gesture_get_last_updated_sequence (GtkGesture *gesture) +{ + GtkGesturePrivate *priv; + + g_return_val_if_fail (GTK_IS_GESTURE (gesture), NULL); + + priv = gtk_gesture_get_instance_private (gesture); + + return priv->last_sequence; +} + +const GdkEvent * +gtk_gesture_get_last_event (GtkGesture *gesture, + GdkEventSequence *sequence) +{ + GtkGesturePrivate *priv; + PointData *data; + + g_return_val_if_fail (GTK_IS_GESTURE (gesture), NULL); + + priv = gtk_gesture_get_instance_private (gesture); + data = g_hash_table_lookup (priv->points, sequence); + + if (!data) + return NULL; + + return data->event; +} + +/** + * gtk_gesture_get_point: + * @gesture: a #GtkGesture + * @sequence: (allow-none): a #GdkEventSequence, or %NULL for pointer events + * @x: (out) (allow-none): return location for X axis of the sequence coordinates + * @y: (out) (allow-none): return location for Y axis of the sequence coordinates + * + * If @sequence is currently being interpreted by @gesture, this + * function returns %TRUE and fills in @x and @y with the last coordinates + * stored for that event sequence. + * + * Returns: %TRUE if @sequence is currently interpreted + * + * Since: 3.14 + **/ +gboolean +gtk_gesture_get_point (GtkGesture *gesture, + GdkEventSequence *sequence, + gdouble *x, + gdouble *y) +{ + GtkGesturePrivate *priv; + PointData *data; + + g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE); + + priv = gtk_gesture_get_instance_private (gesture); + + if (!g_hash_table_lookup_extended (priv->points, sequence, + NULL, (gpointer *) &data)) + return FALSE; + + return gdk_event_get_coords (data->event, x, y); +} + +/** + * gtk_gesture_get_last_update_time: + * @gesture: a #GtkGesture + * @sequence: (allow-none): a #GdkEventSequence, or %NULL for pointer events + * @evtime: (out) (allow-none): return location for last update time + * + * If @sequence is being interpreted by @gesture, this function + * returns %TRUE and fills @evtime with the last event time it + * received from that @sequence. + * + * Returns: %TRUE if @sequence is currently interpreted + * + * Since: 3.14 + **/ +gboolean +gtk_gesture_get_last_update_time (GtkGesture *gesture, + GdkEventSequence *sequence, + guint32 *evtime) +{ + GtkGesturePrivate *priv; + PointData *data; + + g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE); + + priv = gtk_gesture_get_instance_private (gesture); + + if (!g_hash_table_lookup_extended (priv->points, sequence, + NULL, (gpointer *) &data)) + return FALSE; + + if (evtime) + *evtime = gdk_event_get_time (data->event); + + return TRUE; +}; + +/** + * gtk_gesture_get_last_event_type: + * @gesture: a #GtkGesture + * @sequence: a #GdkEventSequence + * + * Returns the last event type that was processed for @sequence. + * + * Returns: the last event type. + * + * Since: 3.14 + **/ +GdkEventType +gtk_gesture_get_last_event_type (GtkGesture *gesture, + GdkEventSequence *sequence) +{ + GtkGesturePrivate *priv; + PointData *data; + + g_return_val_if_fail (GTK_IS_GESTURE (gesture), GDK_NOTHING); + + priv = gtk_gesture_get_instance_private (gesture); + + if (!g_hash_table_lookup_extended (priv->points, sequence, + NULL, (gpointer *) &data)) + return GDK_NOTHING; + + return data->event->type; +} + +/** + * gtk_gesture_get_bounding_box: + * @gesture: a #GtkGesture + * @rect: (out): bounding box containing all active touches. + * + * If there are touch sequences being currently handled by @gesture, + * this function returns #TRUE and fills in @rect with the bounding + * box containing all active touches. Otherwise, #FALSE will be + * returned. + * + * Returns: #TRUE if there are active touches, #FALSE otherwise + * + * Since: 3.14 + **/ +gboolean +gtk_gesture_get_bounding_box (GtkGesture *gesture, + GdkRectangle *rect) +{ + GtkGesturePrivate *priv; + gdouble x1, y1, x2, y2; + GHashTableIter iter; + guint n_points = 0; + PointData *data; + + g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE); + g_return_val_if_fail (rect != NULL, FALSE); + + priv = gtk_gesture_get_instance_private (gesture); + x1 = y1 = G_MAXDOUBLE; + x2 = y2 = -G_MAXDOUBLE; + + g_hash_table_iter_init (&iter, priv->points); + + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &data)) + { + gdouble x, y; + + if (data->state == GTK_EVENT_SEQUENCE_DENIED) + continue; + if (data->event->type == GDK_TOUCH_END || + data->event->type == GDK_BUTTON_RELEASE) + continue; + + gdk_event_get_coords (data->event, &x, &y); + n_points++; + x1 = MIN (x1, x); + y1 = MIN (y1, y); + x2 = MAX (x2, x); + y2 = MAX (y2, y); + } + + if (n_points == 0) + return FALSE; + + rect->x = x1; + rect->y = y1; + rect->width = x2 - x1; + rect->height = y2 - y1; + + return TRUE; +} + + +/** + * gtk_gesture_get_bounding_box_center: + * @gesture: a #GtkGesture + * @x: (out): X coordinate for the bounding box center + * @y: (out): Y coordinate for the bounding box center + * + * If there are touch sequences being currently handled by @gesture, + * this function returns #TRUE and fills in @x and @y with the center + * of the bounding box containing all active touches. Otherwise, #FALSE + * will be returned. + * + * Returns: #FALSE if no active touches are present, #TRUE otherwise + * + * Since: 3.14 + **/ +gboolean +gtk_gesture_get_bounding_box_center (GtkGesture *gesture, + gdouble *x, + gdouble *y) +{ + GdkRectangle rect; + + g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE); + g_return_val_if_fail (x != NULL && y != NULL, FALSE); + + if (!gtk_gesture_get_bounding_box (gesture, &rect)) + return FALSE; + + *x = rect.x + rect.width / 2; + *y = rect.y + rect.height / 2; + return TRUE; +} + +/** + * gtk_gesture_is_active: + * @gesture: a #GtkGesture + * + * Returns %TRUE if the gesture is currently active. + * A gesture is active meanwhile there are touch sequences + * interacting with it. + * + * Returns: %TRUE if gesture is active. + * + * Since: 3.14 + **/ +gboolean +gtk_gesture_is_active (GtkGesture *gesture) +{ + GtkGesturePrivate *priv; + + g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE); + + priv = gtk_gesture_get_instance_private (gesture); + + return g_hash_table_size (priv->points) != 0; +} + +/** + * gtk_gesture_is_recognized: + * @gesture: a #GtkGesture + * + * Returns %TRUE if the gesture is currently recognized. + * A gesture is recognized if there are as many interacting + * touch sequences as required by @gesture, and #GtkGesture::check + * returned #TRUE for the sequences being currently interpreted. + * + * Returns: %TRUE if gesture is recognized. + * + * Since: 3.14 + **/ +gboolean +gtk_gesture_is_recognized (GtkGesture *gesture) +{ + GtkGesturePrivate *priv; + + g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE); + + priv = gtk_gesture_get_instance_private (gesture); + + return priv->recognized; +} + +/** + * gtk_gesture_check: + * @gesture: a #GtkGesture + * + * Triggers a check on the @gesture, this should be rarely needed, + * as the gesture will check the state after handling any event. + * + * Returns: #TRUE if the gesture is recognized. + * + * Since: 3.14 + **/ +gboolean +gtk_gesture_check (GtkGesture *gesture) +{ + GtkGesturePrivate *priv; + + g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE); + + priv = gtk_gesture_get_instance_private (gesture); + + return _gtk_gesture_check_recognized (gesture, priv->last_sequence); +} + +/** + * gtk_gesture_get_touch_only: + * @gesture: a #GtkGesture + * + * Returns #TRUE if the gesture is only triggered by touch events. + * + * Returns: #TRUE if the gesture only handles touch events. + * + * Since: 3.14 + **/ +gboolean +gtk_gesture_get_touch_only (GtkGesture *gesture) +{ + GtkGesturePrivate *priv; + + g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE); + + priv = gtk_gesture_get_instance_private (gesture); + + return priv->touch_only; +} + +/** + * gtk_gesture_set_touch_only: + * @gesture: a #GtkGesture + * @touch_only: whether @gesture handles only touch events + * + * If @touch_only is #TRUE, @gesture will only handle events of type + * #GDK_TOUCH_BEGIN, #GDK_TOUCH_UPDATE or #GDK_TOUCH_END. If #FALSE, + * mouse events will be handled too. + * + * Since: 3.14 + **/ +void +gtk_gesture_set_touch_only (GtkGesture *gesture, + gboolean touch_only) +{ + GtkGesturePrivate *priv; + + g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE); + + priv = gtk_gesture_get_instance_private (gesture); + + touch_only = touch_only != FALSE; + + if (priv->touch_only == touch_only) + return; + + priv->touch_only = touch_only; + g_object_notify (G_OBJECT (gesture), "touch-only"); + _gtk_gesture_update_evmask (gesture); +} + +/** + * gtk_gesture_handles_sequence: + * @gesture: a #GtkGesture + * @sequence: a #GdkEventSequence + * + * Returns #TRUE if @gesture is currently handling events corresponding to + * @sequence. + * + * Returns: #TRUE if @gesture is handling @sequence. + * + * Since: 3.14 + **/ +gboolean +gtk_gesture_handles_sequence (GtkGesture *gesture, + GdkEventSequence *sequence) +{ + GtkGesturePrivate *priv; + + g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE); + + priv = gtk_gesture_get_instance_private (gesture); + + return g_hash_table_contains (priv->points, sequence); +} diff --git a/gtk/gtkgesture.h b/gtk/gtkgesture.h new file mode 100644 index 0000000000..a9296c73fa --- /dev/null +++ b/gtk/gtkgesture.h @@ -0,0 +1,133 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2012, One Laptop Per Child. + * Copyright (C) 2014, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author(s): Carlos Garnacho + */ +#ifndef __GTK_GESTURE_H__ +#define __GTK_GESTURE_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_GESTURE (gtk_gesture_get_type ()) +#define GTK_GESTURE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_GESTURE, GtkGesture)) +#define GTK_GESTURE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_GESTURE, GtkGestureClass)) +#define GTK_IS_GESTURE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_GESTURE)) +#define GTK_IS_GESTURE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_GESTURE)) +#define GTK_GESTURE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_GESTURE, GtkGestureClass)) + +typedef struct _GtkGesture GtkGesture; +typedef struct _GtkGestureClass GtkGestureClass; + +struct _GtkGesture +{ + GtkEventController parent_instance; +}; + +struct _GtkGestureClass +{ + GtkEventControllerClass parent_class; + + gboolean (* check) (GtkGesture *gesture); + + void (* begin) (GtkGesture *gesture, + GdkEventSequence *sequence); + void (* update) (GtkGesture *gesture, + GdkEventSequence *sequence); + void (* end) (GtkGesture *gesture, + GdkEventSequence *sequence); + + void (* sequence_state_changed) (GtkGesture *gesture, + GdkEventSequence *sequence, + GtkEventSequenceState state); + + /*< private >*/ + gpointer padding[10]; +}; + +GDK_AVAILABLE_IN_3_14 +GType gtk_gesture_get_type (void) G_GNUC_CONST; + +GDK_AVAILABLE_IN_3_14 +GdkDevice * gtk_gesture_get_device (GtkGesture *gesture); + +GDK_AVAILABLE_IN_3_14 +GtkEventSequenceState + gtk_gesture_get_sequence_state (GtkGesture *gesture, + GdkEventSequence *sequence); +GDK_AVAILABLE_IN_3_14 +gboolean gtk_gesture_set_sequence_state (GtkGesture *gesture, + GdkEventSequence *sequence, + GtkEventSequenceState state); +GDK_AVAILABLE_IN_3_14 +GList * gtk_gesture_get_sequences (GtkGesture *gesture); + +GDK_AVAILABLE_IN_3_14 +GdkEventSequence * gtk_gesture_get_last_updated_sequence + (GtkGesture *gesture); + +GDK_AVAILABLE_IN_3_14 +gboolean gtk_gesture_handles_sequence (GtkGesture *gesture, + GdkEventSequence *sequence); + +GDK_AVAILABLE_IN_3_14 +const GdkEvent * + gtk_gesture_get_last_event (GtkGesture *gesture, + GdkEventSequence *sequence); +GDK_AVAILABLE_IN_3_14 +gboolean gtk_gesture_get_point (GtkGesture *gesture, + GdkEventSequence *sequence, + gdouble *x, + gdouble *y); +GDK_AVAILABLE_IN_3_14 +gboolean gtk_gesture_get_last_update_time (GtkGesture *gesture, + GdkEventSequence *sequence, + guint32 *evtime); + +GDK_AVAILABLE_IN_3_14 +gboolean gtk_gesture_get_bounding_box (GtkGesture *gesture, + GdkRectangle *rect); +GDK_AVAILABLE_IN_3_14 +gboolean gtk_gesture_get_bounding_box_center + (GtkGesture *gesture, + gdouble *x, + gdouble *y); +GDK_AVAILABLE_IN_3_14 +gboolean gtk_gesture_is_active (GtkGesture *gesture); + +GDK_AVAILABLE_IN_3_14 +gboolean gtk_gesture_is_recognized (GtkGesture *gesture); + +GDK_AVAILABLE_IN_3_14 +gboolean gtk_gesture_check (GtkGesture *gesture); + +GDK_AVAILABLE_IN_3_14 +gboolean gtk_gesture_get_touch_only (GtkGesture *gesture); + +GDK_AVAILABLE_IN_3_14 +void gtk_gesture_set_touch_only (GtkGesture *gesture, + gboolean touch_only); + +G_END_DECLS + +#endif /* __GTK_GESTURE_H__ */ diff --git a/gtk/gtkmarshalers.list b/gtk/gtkmarshalers.list index 31977fe845..ab6983dbe8 100644 --- a/gtk/gtkmarshalers.list +++ b/gtk/gtkmarshalers.list @@ -52,6 +52,7 @@ OBJECT:VOID STRING:DOUBLE STRING:STRING VOID:DOUBLE +VOID:DOUBLE,DOUBLE VOID:BOOLEAN VOID:BOOLEAN,BOOLEAN,BOOLEAN VOID:BOXED -- 2.30.2